﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using Domain.Model.Abstract;
using Domain.Model.Interfaces;

using NHibernate;
using NHibernate.Engine;

using DataAccess.Utils;
using DataAccess.Exceptions;

using FluentNHibernate.Mapping;

namespace DataAccess.Repositories
{
    public abstract class AbstractRepository<TEntity> : ClassMap<TEntity> where TEntity : Entity
    {
        /// <summary>
        /// Type of exception, that should be thrown in case of a failure
        /// </summary>
        public string ExceptionType { get; private set; }

        /// <summary>
        /// Name of this entity the implementing Repository is dealing with
        /// </summary>
        public string NameOfEntity { get; private set; }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="type"></param>
        /// <param name="nameOfEntity"></param>
        public AbstractRepository(string type, string nameOfEntity)
        {
            this.ExceptionType = type;
            this.NameOfEntity = nameOfEntity;
        }

        /// <summary>
        /// GetEntityById
        /// </summary>
        /// <param name="id"></param>
        /// <returns>An object of a class that inherits from Entity and has a matching id</returns>
        public TEntity GetById(int id)
        {
            using (ISession session = SessionProvider.OpenSession())
            {
                try
                {
                    return session.Get<TEntity>(id);
                }
                catch (Exception e)
                {
                    throw new DataRepositoryException(e.Message,
                        ExceptionType, "Could not get the " + NameOfEntity + " with the id: " + id);
                }
            }
        }

        /// <summary>
        /// GetAll
        /// </summary>
        /// <returns>A list of entities of the same type</returns>
        public IList<TEntity> GetAll()
        {
            using (ISession session = SessionProvider.OpenSession())
            {
                try
                {
                    ICriteria criteria = session.CreateCriteria<TEntity>();
                    return criteria.List<TEntity>();
                }
                catch (Exception e)
                {
                    throw new DataRepositoryException(e.Message,
                        ExceptionType, "Could not get all " + NameOfEntity);
                }
            }
        }

        /// <summary>
        /// Add - will add a new entity to the database
        /// </summary>
        /// <param name="entity"></param>
        public void Add(TEntity entity)
        {
            using (ISession session = SessionProvider.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    try
                    {
                        session.Save(entity);
                        session.Flush();
                        transaction.Commit();
                    }
                    catch (Exception e)
                    {
                        transaction.Rollback();

                        throw new DataRepositoryException(e.Message,
                            ExceptionType, "Could not add the " + NameOfEntity);
                    }
                }
            }
        }

        /// <summary>
        /// Delete - will delete an entity from the database
        /// </summary>
        /// <param name="entity"></param>
        public void Delete(TEntity entity)
        {
            using (ISession session = SessionProvider.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    try
                    {
                        session.Delete(entity);
                        session.Flush();
                        transaction.Commit();
                    }
                    catch (Exception e)
                    {
                        transaction.Rollback();

                        throw new DataRepositoryException(e.Message,
                            ExceptionType, "Could not delete the " + NameOfEntity);
                    }
                }
            }
        }

        /// <summary>
        /// Update - will update an entity in the database
        /// </summary>
        /// <param name="entity"></param>
        public void Update(TEntity entity)
        {
            using (ISession session = SessionProvider.OpenSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    try
                    {
                        session.SaveOrUpdate(entity);
                        session.Flush();
                        transaction.Commit();
                    }
                    catch (Exception e)
                    {
                        transaction.Rollback();

                        throw new DataRepositoryException(e.Message,
                            ExceptionType, "Could not update the " + NameOfEntity);
                    }
                }
            }
        }
    }
}